package com.anjingsi.tools.calculation;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.Stack;

/**
 * @program: 计算表达式
 * @description
 * @author: 安静思
 * @create: 2019-12-18 11:47
 **/
@Slf4j
public class Claculator {

    /**
     * 数栈
     */
    private Stack<Double> numberStack = new Stack<>();

    /**
     * 符号栈
     */
    private Stack<Operator> operatorStack = new Stack<>();

    public Double compute(String expression){
        expression = preProcessing(expression);
        String temp = "";
        for (int i = 0; i < expression.length(); i++) {
            String val = expression.substring(i,i+1);
            if(!Operator.isOperator(val) || ".".equals(val)){
                temp += val;
                //最后一位数字的时候
                if(i == expression.length()-1){
                    numberStack.push(Double.valueOf(temp));
                }
                continue;
            }
            if(StringUtils.isNotBlank(temp)){
                numberStack.push(Double.valueOf(temp));
                temp = "";
            }
            Operator operator = Operator.getOperator(val);
            //操作栈为空或者遇到（的操作符直接放入到操作栈中
            if(operatorStack.empty() || operator == Operator.LEFT_BRACKET){
                operatorStack.push(operator);
                continue;
            }
            //遇到右括号开始计算
            if(!operatorStack.empty() && operator == Operator.RIGHT_BRACKET){
                while (operatorStack.peek() != Operator.LEFT_BRACKET && numberStack.size() > 2){
                    compute();
                }
                //不合法的表达式
                if(operatorStack.pop() != Operator.LEFT_BRACKET){
                    throw new IllegalArgumentException("不合法的表达式");
                }
                continue;
            }
            if(operator.getPriority() <= operatorStack.peek().getPriority()){
                if (numberStack.size() < 2) {
                    throw new IllegalArgumentException("不合法的表达式");
                }
                while (operatorStack.size() > 0 && numberStack.size() >= 2 && operator.getPriority() <= operatorStack.peek().getPriority()) {
                    compute();
                }
                operatorStack.push(operator);
            }else {
                operatorStack.push(operator);
            }
        }
        while (operatorStack.size() > 0 && numberStack.size() >= 2){
            compute();
        }
        if(operatorStack.isEmpty() && numberStack.size() == 1){
            return numberStack.pop();
        }
        throw new IllegalArgumentException("不合法的表达式");
    }

    /**
     * 预处理
     * @param expression
     * @return
     */
    private String preProcessing(String expression) {
        //去掉表达式中的所有的空格
        expression = expression.replaceAll(" ","");
        //将其中的中文符号替换成英文符号
        expression = expression.replaceAll("（","(");
        expression = expression.replaceAll("）",")");
        return expression;
    }

    /**
     * 计算两个数的值
     */
    public void compute(){
        //弹出运算符和两个数组进行计算
        double n1 = numberStack.pop();
        double n2 = numberStack.pop();
        Operator operator = operatorStack.pop();
        double resTmp = operator.compute(n2, n1);
//        log.info("计算{}与{}的值：操作符为：{}，计算的结果为：{} ",n1,n2,operator.getValue(),resTmp);
        numberStack.push(resTmp);
    }

    public static void main(String[] args) {
        String expression = "20.2 + 2 * 30.3";
        Long time = System.currentTimeMillis();
        Claculator claculator = new Claculator();
        double result1 = claculator.compute(expression);
        Long time1 = System.currentTimeMillis();
        log.info("方法一计算的结果为：{},耗时：{}",result1,time1-time);

        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn");
        try {
            String result = String.valueOf(scriptEngine.eval(expression));
            Long time2 = System.currentTimeMillis();
            log.info("方法二计算的结果为：{},耗时：{}",result,time2-time1);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }



}
